#include <unistd.h>
#include <getopt.h>

#include "Enesim.h"
#include "Escen.h"
#if HAVE_OPENCL
#include "Enesim_OpenCL.h"
#endif

static double get_time(void)
{
	struct timeval timev;

	gettimeofday(&timev, NULL);
	return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000);
}

static double run(Enesim_Renderer *r, Enesim_Surface *dst, Eina_List *areas, int times)
{
	Enesim_Log *error = NULL;
	uint32_t *d;
	int t;
	double start, end, performance;

	start = get_time();
	for (t = 0; t < times; t++)
	{
		if (!enesim_renderer_draw_list(r, dst, areas, 0, 0, &error))
		{
			break;
		}
	}
	end = get_time();
	performance = end - start;
	if (error)
	{
		enesim_log_dump(error);
		enesim_log_delete(error);
	}

	return performance;
}

static Eina_List * split(int width, int height)
{
	Eina_List *ret = NULL;
	int i, j;
	int aw, ah;
	const int div = 4;

	aw = width / div;
	ah = height / div;
	for (i = 0; i < div; i++)
		for (j = 0; j < div; j++)
	{
		Eina_Rectangle *area;

		area = malloc(sizeof(Eina_Rectangle));
		area->x = i * aw;
		area->y = j * ah;
		area->w = aw;
		area->h = ah;

		ret = eina_list_append(ret, area);
	}
	return ret;
}


static void help(void)
{
	printf("Usage: benchmark OPTIONS\n");
	printf("Where OPTIONS can be on of the following\n");
	printf("-d, --debug Dump the result into a png file\n");
	printf("-h, --help This help message\n");
	printf("-t, --times N Number of times the renderer should be drawn\n");
	printf("-o, --odir The output directory to write the dump file in case it is enabled\n");
	printf("-i, --idir The input directory to get files from in case the renderer needs access to an external file\n");
	printf("-f, --file The .escen file to benchmark\n");
	printf("-b, --boundings If it is set, the dump file will have a surrounding box with the bounding box of the renderer\n");
	printf("-s, --splitted If it is set, the whole area is rendered by drawing sub areas\n");
}

int main(int argc, char **argv)
{
	Escen *e;
	Escen_Ender *ee;
	Escen_Instance *eei;
	Ender_Element *ender;
	Enesim_Renderer *r;
	Enesim_Surface *dst;
	Enesim_Renderer *renderer;
	Enesim_Pool *pool;
	Eina_List *areas = NULL;

	double performance;

	Enesim_Backend backend = ENESIM_BACKEND_SOFTWARE;
	int boundings = 0;
	int debug = 0;
	int width = 256;
	int height = 256;
	int times = 1;
	char *odir = "./";
	char *idir = "./";
	char *file = NULL;
	int splitted = 0;
	char *short_options = "dht:o:i:w:e:f:bsk:";
	struct option long_options[] = {
		{"debug", 0, 0, 'd'},
		{"help", 0, 0, 'h'},
		{"times", 1, 0, 't'},
		{"odir", 1, 0, 'o'},
		{"idir", 1, 0, 'i'},
		{"width", 1, 0, 'w'},
		{"height", 1, 0, 'e'},
		{"file", 1, 0, 'f'},
		{"boundings", 0, 0, 'b'},
		{"splitted", 0, 0, 's'},
		{"k", 1, 0, 'k'},
		{0, 0, 0, 0}
	};
	int c;
	int option;

	/* handle the parameters */
	while ((c = getopt_long(argc, argv, short_options, long_options,
			&option)) != -1)
	{
		switch (c)
		{
			case 'h':
			help();
			return 0;

			case 'd':
			debug = 1;
			break;

			case 't':
			times = atoi(optarg);
			break;

			case 'o':
			odir = strdup(optarg);
			break;

			case 'i':
			idir = strdup(optarg);
			break;

			case 'w':
			width = atoi(optarg);
			break;

			case 'e':
			height = atoi(optarg);
			break;

			case 'f':
			file = strdup(optarg);
			break;

			case 'b':
			boundings = 1;
			break;

			case 's':
			splitted = 1;
			break;

			case 'k':
			if (!strcmp(optarg, "opengl"))
				backend = ENESIM_BACKEND_OPENGL;
			else if (!strcmp(optarg, "opencl"))
				backend = ENESIM_BACKEND_OPENCL;
			else
				backend = ENESIM_BACKEND_SOFTWARE;
			break;

			default:
			break;
		}
	}

	if (!file)
	{
		help();
		return 0;
	}

	escen_init();
	ender_loader_load_all();
	enesim_init();

	/* FIXME make this configurable through cmd line options */
	switch (backend)
	{
		case ENESIM_BACKEND_OPENCL: 
#if HAVE_OPENCL
		pool = enesim_pool_opencl_new();
		dst = enesim_surface_new_pool_from(ENESIM_FORMAT_ARGB8888, width, height, pool);
#endif
		break;
		case ENESIM_BACKEND_OPENGL:
#if HAVE_OPENGL
		pool = enesim_pool_opengl_new();
		dst = enesim_surface_new_pool_from(ENESIM_FORMAT_ARGB8888, width, height, pool);
#endif
		break;

		default:
		dst = enesim_surface_new(ENESIM_FORMAT_ARGB8888, width, height);
		break;
	}
	if (!dst)
	{
		printf("Impossible to create the surface\n");
	}

	e = escen_parser_load(file);
	if (!e)
	{
		printf("Impossible to parse the file %s\n", file);
		return 1;
	}
	escen_surface_path_append(e, idir);

	/* get the first ender */
	/* TODO We must be able to pass the ender we want to test */
	ee = escen_ender_nth_get(e, 0);
	if (!ee)
	{
		printf("No escen ender available\n");
		return 1;
	}
	eei = escen_instance_new(ee, EINA_TRUE);
	if (!eei)
	{
		printf("Not possible to create the instance\n");
		return 1;
	}
	ender = escen_instance_ender_get(eei);
	renderer = ender_element_object_get(ender);
	if (!renderer) return 1;

	if (splitted)
		areas = split(256, 256);
	performance = run(renderer, dst, areas, times);
	if (debug)
	{
		Eina_Array *array;
		char fout[PATH_MAX];
		char *dup;
		char *tmp;


		dup = strdup(file);
		array = eina_file_split(dup);
		tmp = eina_array_pop(array);
		eina_array_free(array);
		snprintf(fout, PATH_MAX, "%s/%s", odir, tmp);
		free(dup);

		for (tmp = fout + strlen(fout); tmp != fout; tmp--)
		{
			if (*tmp == '.')
			{
				strcpy(tmp + 1, "png");
				break;
			}
		}
		printf("Saving %s\n", fout);
		if (boundings)
		{
			Enesim_Renderer *bbox;
			Eina_Rectangle rect;
			Eina_List *l;
			Eina_Rectangle *area;

			bbox = enesim_renderer_rectangle_new();
			enesim_renderer_destination_bounds(renderer, &rect, 0, 0);
			enesim_renderer_y_origin_set(bbox, rect.y);
			enesim_renderer_x_origin_set(bbox, rect.x);
			enesim_renderer_rectangle_width_set(bbox, rect.w);
			enesim_renderer_rectangle_height_set(bbox, rect.h);
			enesim_renderer_shape_stroke_weight_set(bbox, 1);
			enesim_renderer_shape_draw_mode_set(bbox, ENESIM_SHAPE_DRAW_MODE_STROKE);
			enesim_renderer_rop_set(bbox, ENESIM_BLEND);
			enesim_renderer_shape_stroke_color_set(bbox, 0xff000000);
			enesim_renderer_draw(bbox, dst, NULL, 0, 0, NULL);
			printf("Bounding %d %d %d %d\n", rect.x, rect.y, rect.w, rect.h);
			EINA_LIST_FOREACH(areas, l, area)
			{
				printf("Area %d %d %d %d\n", area->x, area->y, area->w, area->h);
				enesim_renderer_y_origin_set(bbox, area->x);
				enesim_renderer_x_origin_set(bbox, area->y);
				enesim_renderer_rectangle_width_set(bbox, area->w);
				enesim_renderer_rectangle_height_set(bbox, area->h);
				enesim_renderer_draw(bbox, dst, NULL, 0, 0, NULL);
			}
		}
		enesim_image_file_save(fout, dst, NULL);
	}
	printf("%-40s [%3.3f sec]\n", file, performance);
	escen_shutdown();
	enesim_shutdown();

	return 0;
}
